/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.eclipse.che.ide.util.browser;

import org.eclipse.che.ide.util.Box;
import com.google.gwt.core.client.GWT;


/** Class to contain run-time checks of a user-agent's capabilities. */
public class UserAgentRuntimeProperties {
    private static final UserAgentRuntimeProperties INSTANCE = createInstance();

    private static UserAgentRuntimeProperties createInstance() {
        return GWT.isScript() ? new UserAgentRuntimeProperties(getNativeUserAgent())
                              : new UserAgentRuntimeProperties("");
    }

    public static UserAgentRuntimeProperties get() {
        return INSTANCE;
    }

    private final String  userAgent;
    private final int     version;
    private final boolean isMac;
    private final boolean isWin;
    private final boolean isLinux;
    private final boolean isIe7;
    private final boolean isIe8;
    private final boolean isChrome;

    public UserAgentRuntimeProperties(String userAgent) {
        this.userAgent = userAgent;
        this.version = calculateVersion(userAgent);
        this.isMac = calculateIsMac(userAgent);
        this.isWin = calculateIsWin(userAgent);
        this.isLinux = calculateIsLinux(userAgent);
        this.isIe7 = calculateIe7(userAgent);
        this.isIe8 = calculateIe8(userAgent);
        this.isChrome = calculateIsChrome(userAgent);
    }

    public String getUserAgent() {
        return userAgent;
    }

    public boolean isMac() {
        return isMac;
    }

    public boolean isWin() {
        return isWin;
    }

    public boolean isLinux() {
        return isLinux;
    }

    public boolean isIe7() {
        return isIe7;
    }

    public boolean isIe8() {
        return isIe8;
    }

    public boolean isChrome() {
        return isChrome;
    }

    /**
     * @return whether the current user agent version is at least the one given by
     *         the method parameters.
     */
    public boolean isAtLeastVersion(int major, int minor) {
        return version >= (major * 1000 + minor);
    }

    /**
     * Do not use this for program logic - for debugging only. For program logic,
     * instead use {@link #isAtLeastVersion(int, int)}
     */
    public int getMajorVer() {
        return version / 1000;
    }

    /**
     * Do not use this for program logic - for debugging only. For program logic,
     * instead use {@link #isAtLeastVersion(int, int)}
     */
    public int getMinorVer() {
        return version % 1000;
    }

    private static native String getNativeUserAgent() /*-{
        return navigator.userAgent;
    }-*/;

    private static boolean calculateIe7(String userAgent) {
        return userAgent.indexOf(" MSIE 7.") != -1;
    }

    private static boolean calculateIe8(String userAgent) {
        return userAgent.indexOf(" MSIE 8.") != -1;
    }

    private static boolean calculateIsMac(String userAgent) {
        return userAgent.indexOf("Mac") != -1;
    }

    private static boolean calculateIsWin(String userAgent) {
        return userAgent.indexOf("Windows") != -1;
    }

    private static boolean calculateIsLinux(String userAgent) {
        return userAgent.indexOf("Linux") != -1;
    }

    private static boolean calculateIsChrome(String userAgent) {
        return userAgent.indexOf("Chrome") != -1;
    }

    private static int calculateVersion(String userAgent) {
        if (userAgent == null || userAgent.isEmpty()) {
            return -1;
        }

//  TODO(user): Make this work after regex deps are fixed and don't break static rendering
//
//    String regexps[] = {"firefox.([0-9]+).([0-9]+)",
//                        "webkit.([0-9]+).([0-9]+)",
//                        "msie.([0-9]+).([0-9]+)",
//                        "minefield.([0-9]+).([0-9]+)"};


//  TODO(user): Don't use "firefox" and "minefield", check Gecko rv.
        String names[] = {"firefox", "webkit", "msie", "minefield"};

        for (String name : names) {
            int v = calculateVersion(name, userAgent);
            if (v >= 0) {
                return v;
            }
        }
        return -1;
    }

//  /**
//   * Matches a browser-specific regular expression against the user agent to
//   * obtain a version number.
//   *
//   * @param regexp The browser-specific regular expression to use
//   * @param userAgent The user agent string to check
//   * @return A version number or -1 if unknown
//   */

    /**
     * Matches a browser-specific name against the user agent to obtain a version
     * number.
     *
     * @param name
     *         The browser-specific name to use
     * @param userAgent
     *         The user agent string to check
     * @return A version number or -1 if unknown
     */
    private static int calculateVersion(String name, String userAgent) {
        int index = userAgent.toLowerCase().indexOf(name);
        if (index == -1) {
            return -1;
        }

        Box<Integer> output = Box.create();

        index += name.length() + 1;

        if ((index = consumeDigit(index, userAgent, output)) == -1) {
            return -1;
        }
        int major = output.boxed;

        index++;

        if ((index = consumeDigit(index, userAgent, output)) == -1) {
            return -1;
        }
        int minor = output.boxed;

        return major * 1000 + minor;

//  TODO(user): Make this work after regex deps are fixed and don't break static rendering
//
//    RegExp pattern = RegExp.compile(regexp);
//    MatchResult result = pattern.exec(userAgent.toLowerCase());
//    if (result != null && result.getGroupCount() == 3) {
//      int major = Integer.parseInt(result.getGroup(1));
//      int minor = Integer.parseInt(result.getGroup(2));
//      return major * 1000 + minor;
//    }
//    return -1;
    }

    private static int consumeDigit(int index, String str, Box<Integer> output) {
        StringBuilder nb = new StringBuilder();

        char c;
        while (index < str.length() && Character.isDigit((c = str.charAt(index)))) {
            nb.append(c);
            index++;
        }

        if (nb.length() == 0) {
            return -1;
        }

        try {
            output.boxed = Integer.parseInt(nb.toString());
            return index;
        } catch (NumberFormatException e) {
            return -1;
        }
    }

}
